home *** CD-ROM | disk | FTP | other *** search
/ United Public Domain Gold 4 / United Public Domain Gold 4.iso / fredfish / ff.0042.dms / ff.0042.adf / MG1a / search.c < prev    next >
C/C++ Source or Header  |  1986-11-26  |  14KB  |  632 lines

  1. /*
  2.  *         Search commands.
  3.  * The functions in this file implement the
  4.  * search commands (both plain and incremental searches
  5.  * are supported) and the query-replace command.
  6.  *
  7.  * The plain old search code is part of the original
  8.  * MicroEMACS "distribution". The incremental search code,
  9.  * and the query-replace code, is by Rich Ellison.
  10.  */
  11. #include    "def.h"
  12.  
  13. #define SRCH_BEGIN    (0)            /* Search sub-codes.    */
  14. #define    SRCH_FORW    (-1)
  15. #define SRCH_BACK    (-2)
  16. #define SRCH_NOPR    (-3)
  17. #define SRCH_ACCM    (-4)
  18. #define    SRCH_MARK    (-5)
  19.  
  20. typedef struct  {
  21.     int    s_code;
  22.     LINE    *s_dotp;
  23.     int    s_doto;
  24. }    SRCHCOM;
  25.  
  26. static    SRCHCOM    cmds[NSRCH];
  27. static    int    cip;
  28.  
  29. int    srch_lastdir = SRCH_NOPR;        /* Last search flags.    */
  30.  
  31. /*
  32.  * Search forward.
  33.  * Get a search string from the user, and search for it,
  34.  * starting at ".". If found, "." gets moved to just after the
  35.  * matched characters, and display does all the hard stuff.
  36.  * If not found, it just prints a message.
  37.  */
  38. /*ARGSUSED*/
  39. forwsearch(f, n, k) {
  40.     register int    s;
  41.  
  42.     if ((s=readpattern("Search")) != TRUE)
  43.         return (s);
  44.     if (forwsrch() == FALSE) {
  45.         ewprintf("Search failed: \"%s\"", pat);
  46.         return (FALSE);
  47.     }
  48.     srch_lastdir = SRCH_FORW;
  49.     return (TRUE);
  50. }
  51.  
  52. /*
  53.  * Reverse search.
  54.  * Get a search string from the  user, and search, starting at "."
  55.  * and proceeding toward the front of the buffer. If found "." is left
  56.  * pointing at the first character of the pattern [the last character that
  57.  * was matched].
  58.  */
  59. /*ARGSUSED*/
  60. backsearch(f, n, k) {
  61.     register int    s;
  62.  
  63.     if ((s=readpattern("Search backward")) != TRUE)
  64.         return (s);
  65.     if (backsrch() == FALSE) {
  66.         ewprintf("Search failed: \"%s\"", pat);
  67.         return (FALSE);
  68.     }
  69.     srch_lastdir = SRCH_BACK;
  70.     return (TRUE);
  71. }
  72.  
  73. /* 
  74.  * Search again, using the same search string
  75.  * and direction as the last search command. The direction
  76.  * has been saved in "srch_lastdir", so you know which way
  77.  * to go.
  78.  */
  79. /*ARGSUSED*/
  80. searchagain(f, n, k) {
  81.     if (srch_lastdir == SRCH_FORW) {
  82.         if (forwsrch() == FALSE) {
  83.             ewprintf("Search failed: \"%s\"", pat);
  84.             return (FALSE);
  85.         }
  86.         return (TRUE);
  87.     }
  88.     if (srch_lastdir == SRCH_BACK) {
  89.         if (backsrch() == FALSE) {
  90.             ewprintf("Search failed: \"%s\"", pat);
  91.             return (FALSE);
  92.         }
  93.         return (TRUE);
  94.     }
  95.     ewprintf("No last search");
  96.     return (FALSE);
  97. }
  98.  
  99. /*
  100.  * Use incremental searching, initially in the forward direction.
  101.  * isearch ignores any explicit arguments.
  102.  */
  103. /*ARGSUSED*/
  104. forwisearch(f, n, k) {
  105.     return (isearch(SRCH_FORW));
  106. }
  107.  
  108. /*
  109.  * Use incremental searching, initially in the reverse direction.
  110.  * isearch ignores any explicit arguments.
  111.  */
  112. /*ARGSUSED*/
  113. backisearch(f, n, k) {
  114.     return (isearch(SRCH_BACK));
  115. }
  116.  
  117. /*
  118.  * Incremental Search.
  119.  *    dir is used as the initial direction to search.
  120.  *    ^S    switch direction to forward
  121.  *    ^R    switch direction to reverse
  122.  *    ^Q    quote next character (allows searching for ^N etc.)
  123.  *    <ESC>    exit from Isearch
  124.  *    <DEL>    undoes last character typed. (tricky job to do this correctly).
  125.  *    other ^    exit search, don't set mark
  126.  *    else    accumulate into search string
  127.  */
  128. isearch(dir) {
  129.     register int    c;
  130.     register LINE    *clp;
  131.     register int    cbo;
  132.     register int    success;
  133.     int        pptr;
  134.     char        opat[NPAT];
  135.  
  136.     for (cip=0; cip<NSRCH; cip++)
  137.         cmds[cip].s_code = SRCH_NOPR;
  138.     (VOID) strcpy(opat, pat);
  139.     cip = 0;
  140.     pptr = -1;
  141.     clp = curwp->w_dotp;
  142.     cbo = curwp->w_doto;
  143.     is_lpush();
  144.     is_cpush(SRCH_BEGIN);
  145.     success = TRUE;
  146.     is_prompt(dir, TRUE, success);
  147.     for (;;) {
  148.         update();
  149.         switch (c = (char) getkey(KQUOTE)) {
  150.         case METACH:
  151.             srch_lastdir = dir;
  152.             curwp->w_markp = clp;
  153.             curwp->w_marko = cbo;
  154.             if (kbdmop == NULL) ewprintf("Mark set");
  155.             return (TRUE);
  156.  
  157.         case CCHR('G'):
  158.             if (success != TRUE) {
  159.                 while (is_peek() == SRCH_ACCM)
  160.                     if (is_undo(&pptr, &dir) == FALSE)
  161.                         break;
  162.                 success = TRUE;
  163.                 is_prompt(dir, pptr < 0, success);
  164.                 break;
  165.             }
  166.             curwp->w_dotp = clp;
  167.             curwp->w_doto = cbo;
  168.             curwp->w_flag |= WFMOVE;
  169.             srch_lastdir = dir;
  170.             (VOID) ctrlg(FALSE, 0, KRANDOM);
  171.             (VOID) strcpy(pat, opat);
  172.             return ABORT;
  173.  
  174.         case CCHR('S'):
  175.             if (dir == SRCH_BACK) {
  176.                 dir = SRCH_FORW;
  177.                 is_lpush();
  178.                 is_cpush(SRCH_FORW);
  179.                 success = TRUE;
  180.             }
  181.             if (success==FALSE && dir==SRCH_FORW)
  182.                 break;
  183.             is_lpush();
  184.             pptr = strlen(pat);
  185.             (VOID) forwchar(FALSE, 1, KRANDOM);
  186.             if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
  187.             else {
  188.                 (VOID) backchar(FALSE, 1, KRANDOM);
  189.                 ttbeep();
  190.                 success = FALSE;
  191.             }
  192.             is_prompt(dir, pptr < 0, success);
  193.             break;
  194.  
  195.         case CCHR('R'):
  196.             if (dir == SRCH_FORW) {
  197.                 dir = SRCH_BACK;
  198.                 is_lpush();
  199.                 is_cpush(SRCH_BACK);
  200.                 success = TRUE;
  201.             }
  202.             if (success==FALSE && dir==SRCH_BACK)
  203.                 break;
  204.             is_lpush();
  205.             pptr = strlen(pat);
  206.             (VOID) backchar(FALSE, 1, KRANDOM);
  207.             if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
  208.             else {
  209.                 (VOID) forwchar(FALSE, 1, KRANDOM);
  210.                 ttbeep();
  211.                 success = FALSE;
  212.             }
  213.             is_prompt(dir, pptr < 0, success);
  214.             break;
  215.  
  216.         case 0x7F:
  217.             if (is_undo(&pptr, &dir) != TRUE) return FALSE;
  218.             if (is_peek() != SRCH_ACCM) success = TRUE;
  219.             is_prompt(dir, pptr < 0, success);
  220.             break;
  221.  
  222.         case CCHR('Q'):
  223.             c = (char) getkey(KQUOTE);
  224.             goto  addchar;
  225.         case CCHR('M'):
  226.             c = CCHR('J');
  227.         case CCHR('J'):
  228.             goto  addchar;
  229.  
  230.         default:
  231.             if (ISCTRL(c) != FALSE) {
  232.                 c += '@';
  233.                 c |= KCTRL;
  234.                 success = execute((KEY) c, FALSE, 1);
  235.                 curwp->w_markp = clp;
  236.                 curwp->w_marko = cbo;
  237.                 if (kbdmop == NULL) ewprintf("Mark set");
  238.                 curwp->w_flag |= WFMOVE;
  239.                 return (success);
  240.             }                
  241.         addchar:
  242.             if (pptr == -1)
  243.                 pptr = 0;
  244.             if (pptr == 0)
  245.                 success = TRUE;
  246.             pat[pptr++] = c;
  247.             if (pptr == NPAT) {
  248.                 ewprintf("Pattern too long");
  249.                 return FALSE;
  250.             }
  251.             pat[pptr] = '\0';
  252.             is_lpush();
  253.             if (success != FALSE) {
  254.                 if (is_find(dir) != FALSE)
  255.                     is_cpush(c);
  256.                 else {
  257.                     success = FALSE;
  258.                     ttbeep();
  259.                     is_cpush(SRCH_ACCM);
  260.                 }
  261.             } else
  262.                 is_cpush(SRCH_ACCM);
  263.             is_prompt(dir, FALSE, success);
  264.         }
  265.     }
  266. }
  267.  
  268. is_cpush(cmd) register int cmd; {
  269.     if (++cip >= NSRCH)
  270.         cip = 0;
  271.     cmds[cip].s_code = cmd;
  272. }
  273.  
  274. is_lpush() {
  275.     register int    ctp;
  276.  
  277.     ctp = cip+1;
  278.     if (ctp >= NSRCH)
  279.         ctp = 0;
  280.     cmds[ctp].s_code = SRCH_NOPR;
  281.     cmds[ctp].s_doto = curwp->w_doto;
  282.     cmds[ctp].s_dotp = curwp->w_dotp;
  283. }
  284.  
  285. is_pop() {
  286.     if (cmds[cip].s_code != SRCH_NOPR) {
  287.         curwp->w_doto  = cmds[cip].s_doto; 
  288.         curwp->w_dotp  = cmds[cip].s_dotp;
  289.         curwp->w_flag |= WFMOVE;
  290.         cmds[cip].s_code = SRCH_NOPR;
  291.     }
  292.     if (--cip <= 0)
  293.         cip = NSRCH-1;
  294. }
  295.  
  296. is_peek() {
  297.     return cmds[cip].s_code;
  298. }
  299.  
  300. is_undo(pptr, dir) register int *pptr; register int *dir; {
  301.     register int    redo = FALSE ;
  302.     switch (cmds[cip].s_code) {
  303.     case SRCH_BEGIN:
  304.     case SRCH_NOPR:
  305.         *pptr = -1;
  306.     case SRCH_MARK:
  307.         break;
  308.  
  309.     case SRCH_FORW:
  310.         *dir = SRCH_BACK;
  311.         redo = TRUE;
  312.         break;
  313.  
  314.     case SRCH_BACK:
  315.         *dir = SRCH_FORW;
  316.         redo = TRUE;
  317.         break;
  318.  
  319.     case SRCH_ACCM:
  320.     default:
  321.         *pptr -= 1;
  322.         if (*pptr < 0)
  323.             *pptr = 0;
  324.         pat[*pptr] = '\0';
  325.         break;
  326.     }
  327.     is_pop();
  328.     if (redo) return is_undo(pptr, dir);
  329.     return (TRUE);
  330. }
  331.  
  332. is_find(dir) register int dir; {
  333.     register int    plen, odoto;
  334.     register LINE    *odotp ;
  335.  
  336.     odoto = curwp->w_doto;
  337.     odotp = curwp->w_dotp;
  338.     plen = strlen(pat);
  339.     if (plen != 0) {
  340.         if (dir==SRCH_FORW) {
  341.             (VOID) backchar(TRUE, plen, KRANDOM);
  342.             if (forwsrch() == FALSE) {
  343.                 curwp->w_doto = odoto;
  344.                 curwp->w_dotp = odotp;
  345.                 return (FALSE);
  346.             }
  347.             return (TRUE);
  348.         }
  349.         if (dir==SRCH_BACK) {
  350.             (VOID) forwchar(TRUE, plen, KRANDOM);
  351.             if (backsrch() == FALSE) {
  352.                 curwp->w_doto = odoto;
  353.                 curwp->w_dotp = odotp;
  354.                 return (FALSE);
  355.             }
  356.             return (TRUE);
  357.         }
  358.         ewprintf("bad call to is_find");
  359.         return FALSE;
  360.     }
  361.     return (FALSE);
  362. }
  363.  
  364. /*
  365.  * If called with "dir" not one of SRCH_FORW
  366.  * or SRCH_BACK, this routine used to print an error
  367.  * message. It also used to return TRUE or FALSE,
  368.  * depending on if it liked the "dir". However, none
  369.  * of the callers looked at the status, so I just
  370.  * made the checking vanish.
  371.  */
  372. is_prompt(dir, flag, success) {
  373.     VOID is_dspl();
  374.  
  375.     if (dir == SRCH_FORW) {
  376.         if (success != FALSE)
  377.             is_dspl("I-search", flag);
  378.         else
  379.             is_dspl("Failing I-search", flag);
  380.     } else if (dir == SRCH_BACK) {
  381.         if (success != FALSE)
  382.             is_dspl("I-search backward", flag);
  383.         else
  384.             is_dspl("Failing I-search backward", flag);
  385.     } else ewprintf("Broken call to is_prompt");
  386. }
  387.  
  388. /*
  389.  * Prompt writing routine for the incremental search. 
  390.  * The "prompt" is just a string. The "flag" determines
  391.  * whether pat should be printed.
  392.  */
  393. VOID
  394. is_dspl(prompt, flag) char *prompt; {
  395.  
  396.     if (kbdmop != NULL) return;
  397.     if (flag != FALSE)
  398.         ewprintf("%s: ", prompt);
  399.     else
  400.         ewprintf("%s: %s", prompt, pat);
  401. }
  402.  
  403. /*
  404.  * Query Replace.
  405.  *    Replace strings selectively.  Does a search and replace operation.
  406.  */
  407. /*ARGSUSED*/
  408. queryrepl(f, n, k) {
  409.     register int    s;
  410.     register int    rcnt = 0;    /* Replacements made so far    */
  411.     register int    plen;        /* length of found string    */
  412.     char        news[NPAT];    /* replacement string        */
  413.  
  414.     if ((s=readpattern("Query replace")) != TRUE)
  415.         return (s);
  416.     if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
  417.         return (s);
  418.     if (s == FALSE)
  419.         news[0] = '\0';
  420.     if (kbdmop == NULL) ewprintf("Query replacing %s with %s:", pat, news);
  421.     plen = strlen(pat);
  422.  
  423.     /*
  424.      * Search forward repeatedly, checking each time whether to insert
  425.      * or not.  The "!" case makes the check always true, so it gets put
  426.      * into a tighter loop for efficiency.
  427.      */
  428.  
  429.     while (forwsrch() == TRUE) {
  430.     retry:
  431.         update();
  432.         switch (getkey(KQUOTE)) {
  433.         case ' ':
  434.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  435.                 return (FALSE);
  436.             rcnt++;
  437.             break;
  438.  
  439.         case '.':
  440.             if (lreplace((RSIZE) plen, news, f) == FALSE)
  441.                 return (FALSE);
  442.             rcnt++;
  443.             goto stopsearch;
  444.  
  445.         case CCHR('G'):    /* ^G or ESC */
  446.             (VOID) ctrlg(FALSE, 0, KRANDOM);
  447.         case 033:
  448.             goto stopsearch;
  449.  
  450.         case '!':
  451.             do {
  452.                 if (lreplace((RSIZE) plen, news, f) == FALSE)
  453.                     return (FALSE);
  454.                 rcnt++;
  455.             } while (forwsrch() == TRUE);
  456.             goto stopsearch;
  457.  
  458.         case 0x7F:        /* To not replace */
  459.             break;
  460.  
  461.         default:
  462. ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
  463.             goto retry;
  464.         }
  465.     }
  466. stopsearch:
  467.     curwp->w_flag |= WFHARD;
  468.     update();
  469.     if (kbdmop == NULL) {
  470.         if (rcnt == 0)
  471.             ewprintf("(No replacements done)");
  472.         else if (rcnt == 1)
  473.             ewprintf("(1 replacement done)");
  474.         else
  475.             ewprintf("(%d replacements done)", rcnt);
  476.     }
  477.     return TRUE;
  478. }
  479.  
  480. /*
  481.  * This routine does the real work of a
  482.  * forward search. The pattern is sitting in the external
  483.  * variable "pat". If found, dot is updated, the window system
  484.  * is notified of the change, and TRUE is returned. If the
  485.  * string isn't found, FALSE is returned.
  486.  */
  487. forwsrch() {
  488.     register LINE    *clp;
  489.     register int    cbo;
  490.     register LINE    *tlp;
  491.     register int    tbo;
  492.     register char    *pp;
  493.     register int    c;
  494.  
  495.     clp = curwp->w_dotp;
  496.     cbo = curwp->w_doto;
  497.     while (clp != curbp->b_linep) {
  498.         if (cbo == llength(clp)) {
  499.             clp = lforw(clp);
  500.             cbo = 0;
  501.             c = SEOL;
  502.         } else
  503.             c = lgetc(clp, cbo++);
  504.         if (eq(c, pat[0]) != FALSE) {
  505.             tlp = clp;
  506.             tbo = cbo;
  507.             pp  = &pat[1];
  508.             while (*pp != 0) {
  509.                 if (tlp == curbp->b_linep)
  510.                     goto fail;
  511.                 if (tbo == llength(tlp)) {
  512.                     tlp = lforw(tlp);
  513.                     if (tlp == curbp->b_linep)
  514.                         goto fail;
  515.                     tbo = 0;
  516.                     c = SEOL;
  517.                 } else
  518.                     c = lgetc(tlp, tbo++);
  519.                 if (eq(c, *pp++) == FALSE)
  520.                     goto fail;
  521.             }
  522.             curwp->w_dotp  = tlp;
  523.             curwp->w_doto  = tbo;
  524.             curwp->w_flag |= WFMOVE;
  525.             return (TRUE);
  526.         }
  527.     fail:    ;
  528.     }
  529.     return (FALSE);
  530. }
  531.  
  532. /*
  533.  * This routine does the real work of a
  534.  * backward search. The pattern is sitting in the external
  535.  * variable "pat". If found, dot is updated, the window system
  536.  * is notified of the change, and TRUE is returned. If the
  537.  * string isn't found, FALSE is returned.
  538.  */
  539. backsrch() {
  540.     register LINE    *clp;
  541.     register int    cbo;
  542.     register LINE    *tlp;
  543.     register int    tbo;
  544.     register int    c;
  545.     register char    *epp;
  546.     register char    *pp;
  547.  
  548.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  549.         ;
  550.     clp = curwp->w_dotp;
  551.     cbo = curwp->w_doto;
  552.     for (;;) {
  553.         if (cbo == 0) {
  554.             clp = lback(clp);
  555.             if (clp == curbp->b_linep)
  556.                 return (FALSE);
  557.             cbo = llength(clp)+1;
  558.         }
  559.         if (--cbo == llength(clp))
  560.             c = SEOL;
  561.         else
  562.             c = lgetc(clp,cbo);
  563.         if (eq(c, *epp) != FALSE) {
  564.             tlp = clp;
  565.             tbo = cbo;
  566.             pp  = epp;
  567.             while (pp != &pat[0]) {
  568.                 if (tbo == 0) {
  569.                     tlp = lback(tlp);
  570.                     if (tlp == curbp->b_linep)
  571.                         goto fail;
  572.                     tbo = llength(tlp)+1;
  573.                 }
  574.                 if (--tbo == llength(tlp))
  575.                     c = SEOL;
  576.                 else
  577.                     c = lgetc(tlp,tbo);
  578.                 if (eq(c, *--pp) == FALSE)
  579.                     goto fail;
  580.             }
  581.             curwp->w_dotp  = tlp;
  582.             curwp->w_doto  = tbo;
  583.             curwp->w_flag |= WFMOVE;
  584.             return (TRUE);
  585.         }
  586.     fail:    ;
  587.     }
  588. }
  589.  
  590. /*
  591.  * Compare two characters.
  592.  * The "bc" comes from the buffer.
  593.  * It has its case folded out. The
  594.  * "pc" is from the pattern.
  595.  */
  596. eq(bc, pc) {
  597.     register int    ibc;
  598.     register int    ipc;
  599.  
  600.     ibc = bc & 0xFF;
  601.     ipc = pc & 0xFF;
  602.     if (ISLOWER(ibc) != FALSE)
  603.         ibc = TOUPPER(ibc);
  604.     if (ISLOWER(ipc) != FALSE)
  605.         ipc = TOUPPER(ipc);
  606.     if (ibc == ipc)
  607.         return (TRUE);
  608.     return (FALSE);
  609. }
  610.  
  611. /*
  612.  * Read a pattern.
  613.  * Stash it in the external variable "pat". The "pat" is
  614.  * not updated if the user types in an empty line. If the user typed
  615.  * an empty line, and there is no old pattern, it is an error.
  616.  * Display the old pattern, in the style of Jeff Lomicka. There is
  617.  * some do-it-yourself control expansion.
  618.  */
  619. readpattern(prompt) char *prompt; {
  620.     register int    s;
  621.     char        tpat[NPAT];
  622.  
  623.     if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
  624.     else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
  625.  
  626.     if (s == TRUE)                /* Specified        */
  627.         (VOID) strcpy(pat, tpat);
  628.     else if (s==FALSE && pat[0]!=0)        /* CR, but old one    */
  629.         s = TRUE;
  630.     return (s);
  631. }
  632.